home *** CD-ROM | disk | FTP | other *** search
/ GFX Sensations 1 / Graphic Sensations - Volume 1.iso / tools / amiga / 3d_tools / irit40s.lha / Irit / irit / inptprsr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-30  |  31.3 KB  |  1,071 lines

  1. /*****************************************************************************
  2. *   "Irit" - the 3d (not only polygonal) solid modeller.             *
  3. *                                         *
  4. * Written by:  Gershon Elber                Ver 0.2, Mar. 1990   *
  5. ******************************************************************************
  6. *   Module to convert infix expression given as    ascii stream sequence into   *
  7. * a binary tree, and evaluate it.                         *
  8. *   All the objects are handled the same but the numerical one, which is     *
  9. * moved as a RealType and not as an object (only internally within this         *
  10. * module) as it is frequently used and consumes much less memory this way.   *
  11. *****************************************************************************/
  12.  
  13. #include <stdio.h>
  14. #include <ctype.h>
  15. #include <math.h>
  16. #include <string.h>
  17. #include "program.h"
  18. #include "allocate.h"
  19. #include "ctrl-brk.h"
  20. #include "inptprsg.h"
  21. #include "inptprsl.h"
  22. #include "objects.h"
  23. #include "overload.h"
  24. #include "windows.h"
  25.  
  26. static int IPGlblILastToken;              /* Globals used by parser. */
  27. static InptPrsrEvalErrType
  28.     IPGlblParseError = IPE_NO_ERR;
  29. static UserDefinedFuncDefType
  30.     *IPGlblUserFunc = NULL;
  31. char IPGlblCharData[INPUT_LINE_LEN];          /* Used for both parse & eval. */
  32. static FileStackStruct FileStack[FILE_STACK_SIZE];    /* Include file stack. */
  33. static int
  34.     FileStackPtr = 0;
  35.  
  36. /* Operator preceeding parser stack, and stack pointer: */
  37. static ParseTree *Stack[MAX_PARSER_STACK];
  38. static int
  39.     ParserStackPointer = 0;
  40.  
  41. #ifdef DEBUG1
  42.     int MaxStackPointer = 0;          /* Measure maximum depth of stack. */
  43. #endif /* DEBUG1 */
  44.  
  45. static ParseTree *GenInputParseTree(void);
  46. static ParseTree *OperatorPrecedence(void);
  47. static int TestPreceeding(int Token1, int Token2);
  48. static int GetToken(RealType *Data);
  49. static int GetVarFuncToken(char *Token, RealType *Data);
  50. static int GetEchoSrc(void);
  51. static void InptPrsrUnGetC(char c);
  52. static char InptPrsrGetC(void);
  53.  
  54. /*****************************************************************************
  55. *   Main module routine - generate parse tree and then tries to evaluate it. *
  56. * Returns TRUE if succesfull, otherwise check IPGlblParseError/EvalError.    *
  57. *****************************************************************************/
  58. int InputParser(void)
  59. {
  60.     ParseTree *PTree;
  61.  
  62.     if (GlblFatalError) {
  63.     GlblFatalError = FALSE;
  64.     FlushToEndOfExpr(FALSE);      /* Close all include files if any. */
  65.     return TRUE;
  66.     }
  67.  
  68.     PTree = GenInputParseTree();             /* Generate parse tree. */
  69.  
  70.     if (IPGlblParseError == IPE_NO_ERR) {
  71. #    ifdef DEBUG1
  72.         fprintf(stderr, "\nInput generated Parse tree (Max stack = %d)\n",
  73.                             MaxStackPointer);
  74.         InptPrsrPrintTree(PTree, NULL);
  75.         fprintf(stderr, "\n");
  76. #    endif /* DEBUG1 */
  77.     if (InptPrsrTypeCheck(PTree, 0) == ERROR_EXPR) {   /* Type checking. */
  78.         InptPrsrFreeTree(PTree);             /* Not needed any more. */
  79.         FlushToEndOfExpr(TRUE);/* Close all include files, & flush stdin.*/
  80.         return FALSE;
  81.     }
  82.  
  83.     InptPrsrEvalTree(PTree, 0);                 /* Evaluate it. */
  84.     if (IPGlblEvalError != IPE_NO_ERR) {
  85.         FlushToEndOfExpr(TRUE); /* Close include files, and flush stdin. */
  86.         return FALSE;
  87.     }
  88.     }
  89.     else {
  90.     FlushToEndOfExpr(TRUE); /* Close all include files, and flush stdin. */
  91.     return FALSE;
  92.     }
  93.  
  94.     InptPrsrFreeTree(PTree);                 /* Not needed any more. */
  95.  
  96.     return !(IPGlblParseError || IPGlblEvalError);
  97. }
  98.  
  99. /*****************************************************************************
  100. *   Routine to convert the expression from stream f into a binary tree.      *
  101. * Algorithm: Using operator precedence with the following grammer:           *
  102. * EXPR    ::= EXPR    |  EXPR + EXPR    |  EXPR - EXPR                       *
  103. * EXPR    ::= EXPR    |  EXPR * EXPR    |  EXPR / EXPR                       *
  104. * EXPR    ::= EXPR    |  EXPR ^ EXPR                                         *
  105. * EXPR    ::= EXPR    |  EXPR , EXPR    |  EXPR = EXPR                       *
  106. * EXPR    ::= NUMBER  |  -EXPR          |  (EXPR)        |  FUNCTION         *
  107. * FUCTION ::= FUNC(EXPR , EXPR , ...)                         *
  108. * Where FUNC might be function like arithmetics (SIN, COS etc.).         *
  109. * Note that FUNC might have more than one operand, seperated by ','.         *
  110. *                                                                            *
  111. * Note the stream is terminated by semicolon character ';'.             *
  112. *                                                                            *
  113. * Left associativity for +, -, *, /, ^.                                      *
  114. * Precedence of operators is as usual:                                       *
  115. *     <Highest> {unar minus}   {^}   {*, /}   {+, -} <Lowest>             *
  116. *                                                                            *
  117. * Returns NULL if an error was found, and error is in IPGlblParseError       *
  118. *****************************************************************************/
  119. static ParseTree *GenInputParseTree(void)
  120. {
  121.     ParseTree *Root;
  122.     int i;
  123.  
  124.     IPGlblILastToken = 0;    /* Used to hold last token read from stream. */
  125.     IPGlblParseError = IPE_NO_ERR;             /* No errors so far ... */
  126.  
  127.     Root = OperatorPrecedence();
  128.  
  129.     if (IPGlblParseError) {
  130.     /* Free partialy allocated tree. */
  131.     for (i = 0; i <= ParserStackPointer; i++)
  132.         InptPrsrFreeTree(Stack[i]);
  133.         return NULL;                          /* Error ! */
  134.     }
  135.     else
  136.     return Root;
  137. }
  138.  
  139. /*****************************************************************************
  140. *  Routine to allocate new ParseTree expression node:                 *
  141. *****************************************************************************/
  142. ParseTree *ExprMalloc(void)
  143. {
  144.     ParseTree *p;
  145.  
  146.     p = (ParseTree *) IritMalloc(sizeof(ParseTree));
  147.     p -> Right = p -> Left = NULL;
  148.     p -> NodeKind = IP_OBJ_UNDEF;
  149.     p -> PObj = NULL;
  150.     p -> UserFunc = NULL;
  151.     return p;
  152. }
  153.  
  154. /*****************************************************************************
  155. *  Routine to free one expression node:                         *
  156. *****************************************************************************/
  157. void ExprFree(ParseTree *Ptr)
  158. {
  159.     IritFree((VoidPtr) Ptr);
  160. }
  161.  
  162. /*****************************************************************************
  163. *   Routine to actually parse using operator precedence:                     *
  164. * Few Notes:                                                                 *
  165. * 1. Parse the input with the help of GetToken routine. Input is redirected  *
  166. *    using the FileStack.                             *
  167. * 2. All tokens must be in the range of 0..999 as we use the numbers above   *
  168. *    it (adding 1000) to deactivate them in the handle searching (i.e. when  *
  169. *    they were reduced to sub.-expression).                                  *
  170. * 3. Returns NULL pointer in case of an error.                     *
  171. * 4. See "Compilers - principles, techniques and tools" by Aho, Sethi &      *
  172. *    Ullman,   pages 207-210.                                                *
  173. *****************************************************************************/
  174. static ParseTree *OperatorPrecedence(void)
  175. {
  176.     int Token, LowHandle, Temp1, Temp2;
  177.     RealType Data;
  178.  
  179. #   ifdef DEBUG1
  180.     MaxStackPointer = 0;
  181. #   endif /* DEBUG1 */
  182.  
  183.     ParserStackPointer = 0;
  184.  
  185.     /* Push the start symbol on stack (node pointer points on tos): */
  186.     Stack[ParserStackPointer] = ExprMalloc();
  187.     Stack[ParserStackPointer] -> NodeKind = TOKENSTART;
  188.     Stack[ParserStackPointer] -> Right =
  189.     Stack[ParserStackPointer] -> Left = NULL;
  190.  
  191.     Token = GetToken(&Data);      /* Get one look ahead token to start with. */
  192.  
  193.     while (TRUE) {
  194.         if (IPGlblParseError)
  195.         return NULL;
  196.  
  197.         Temp1 = ParserStackPointer;       /* Find top active token (<1000). */
  198.         while (Stack[Temp1] -> NodeKind >= 1000)
  199.         Temp1--;
  200.         /* Now test to see if the new token completes an handle: */
  201.         if (TestPreceeding(Stack[Temp1] -> NodeKind, Token)) {
  202.             switch (Token) {
  203.         case CLOSPARA:
  204.                     if (Stack[Temp1] -> NodeKind == OPENPARA) {
  205.             ExprFree(Stack[Temp1]);         /* Free open paran. */
  206.             Stack[Temp1] = NULL;
  207.             /* If a parameter is introduced instead of function  */
  208.             /* it will be reduced already against "(" and it     */
  209.             /* probably was missspelled function...             */
  210.                         if (Stack[Temp1-1] -> NodeKind == PARAMETER + 1000) {
  211.                 strcpy(IPGlblCharData,
  212.                     Stack[Temp1-1] -> PObj -> Name);
  213.                 IPGlblParseError = IP_ERR_UNDEF_FUNC;
  214.                 return NULL;
  215.             }
  216.  
  217.             if (IS_USER_FUNCTION(Stack[Temp1 - 1] -> NodeKind)) {
  218.                 if (ParserStackPointer - Temp1 == 1) {
  219.                 Stack[ParserStackPointer] -> NodeKind -= 1000;
  220.                 Stack[Temp1 - 1] -> NodeKind += 1000;
  221.                 Stack[Temp1 - 1] -> Right =
  222.                     Stack[ParserStackPointer];
  223.                 ParserStackPointer -= 2;
  224.                 }
  225.                 else {
  226.                 Stack[Temp1 - 1] -> NodeKind += 1000;
  227.                 Stack[Temp1 - 1] -> Right = NULL;
  228.                 ParserStackPointer--;
  229.                 }
  230.             }
  231.                         else if (IS_NO_PARAM_FUNC(Stack[Temp1 - 1] -> NodeKind)) {
  232.                 if (ParserStackPointer - Temp1 == 1) {
  233.                     UpdateCharError("",
  234.                         Stack[Temp1 - 1] -> NodeKind);
  235.                 IPGlblParseError = IP_ERR_NO_PARAM_FUNC;
  236.                 return NULL;
  237.                 }
  238.                 Stack[Temp1 - 1] -> NodeKind += 1000;
  239.                 Stack[Temp1 - 1] -> Right = NULL;
  240.                 ParserStackPointer--;
  241.                         }
  242.                         else if (IS_FUNCTION(Stack[Temp1 - 1] -> NodeKind)) {
  243.                 if (ParserStackPointer - Temp1 != 1) {
  244.                     UpdateCharError("",
  245.                         Stack[Temp1 - 1] -> NodeKind);
  246.                     IPGlblParseError = IP_ERR_PARAM_FUNC;
  247.                     return NULL;
  248.                 }
  249.                 Stack[ParserStackPointer] -> NodeKind -= 1000;
  250.                 Stack[Temp1 - 1] -> NodeKind += 1000;
  251.                 Stack[Temp1 - 1] -> Right =
  252.                     Stack[ParserStackPointer];
  253.                 ParserStackPointer -= 2;
  254.                     }
  255.                         else {
  256.                 if (ParserStackPointer - Temp1 != 1) {
  257.                     IPGlblParseError = IP_ERR_PARAM_MATCH;
  258.                     return NULL;
  259.                 }
  260.                             Stack[Temp1] = Stack[ParserStackPointer--];
  261.             }
  262.                         Token = GetToken(&Data);       /* Get another token. */
  263.                         continue;
  264.             }
  265.             else if (Stack[Temp1] -> NodeKind == TOKENSTART) {
  266.             /* No match for this one! */
  267.                         IPGlblParseError = IP_ERR_PARAM_MATCH;
  268.             return NULL;
  269.             }
  270.             break;
  271.                 case TOKENEND:
  272.                     if (Stack[Temp1] -> NodeKind == TOKENSTART) {
  273.                         if (ParserStackPointer != 1) {
  274.                             IPGlblParseError = IP_ERR_WRONG_SYNTAX;
  275.                 return NULL;
  276.             }
  277.             InptPrsrFreeTree(Stack[Temp1]);      /* The TOKENSTART. */
  278.             Stack[1] -> NodeKind -= 1000;
  279.             return Stack[1];
  280.             }
  281.         }
  282.  
  283.             Temp2 = Temp1 - 1;          /* Find the lower bound of handle. */
  284.             while (Temp2 >= 0 && Stack[Temp2] -> NodeKind >= 1000)
  285.         Temp2--;
  286.             LowHandle = Temp2 + 1;
  287.             if (LowHandle < 1) {                  /* No low bound was found. */
  288.                 IPGlblParseError = IP_ERR_WRONG_SYNTAX;
  289.             return NULL;             /* We ignore data till now. */
  290.             }
  291.         switch (ParserStackPointer - LowHandle + 1) {
  292.         case 1: /* Its a scalar one - mark it as used (add 1000). */
  293.             switch (Stack[ParserStackPointer] -> NodeKind) {
  294.             case NUMBER:
  295.             case PARAMETER:
  296.             case STRING:
  297.                     Stack[ParserStackPointer] -> NodeKind += 1000;
  298.                 break;
  299.             default:
  300.                 UpdateCharError("Found ",
  301.                 Stack[ParserStackPointer] -> NodeKind);
  302.                 IPGlblParseError = IP_ERR_PARAM_EXPECT;
  303.                 return NULL;
  304.             }
  305.             break;
  306.         case 2: /* Its a monadic operator - create the subtree. */
  307.             switch (Stack[ParserStackPointer-1] -> NodeKind) {
  308.                 case BOOL_NOT:
  309.                 case UNARMINUS:
  310.                     Stack[ParserStackPointer-1] -> Right =
  311.                         Stack[ParserStackPointer];
  312.                     Stack[ParserStackPointer] -> NodeKind -= 1000;
  313.                     Stack[ParserStackPointer-1] -> NodeKind += 1000;
  314.                     ParserStackPointer--;
  315.                     break;
  316.                 case OPENPARA:
  317.                 IPGlblParseError = IP_ERR_PARAM_MATCH;
  318.                 return NULL;
  319.                 default:
  320.                 UpdateCharError("Found Operator ",
  321.                 Stack[ParserStackPointer-1] -> NodeKind);
  322.                 IPGlblParseError = IP_ERR_ONE_OPERAND;
  323.                 return NULL;
  324.             }
  325.             break;
  326.         case 3: /* Its a diadic operator - create the subtree. */
  327.             switch (Stack[ParserStackPointer-1] -> NodeKind) {
  328.                 case PLUS:
  329.                 case MINUS:
  330.                 case MULT:
  331.                 case DIV:
  332.                 case POWER:
  333.                 case COMMA:
  334.                 case EQUAL:
  335.                 case CMP_EQUAL:
  336.                 case CMP_NOTEQUAL:
  337.                 case CMP_LSEQUAL:
  338.                 case CMP_GTEQUAL:
  339.                 case CMP_LESS:
  340.                 case CMP_GREAT:
  341.                 case BOOL_AND:
  342.                 case BOOL_OR:
  343.                 case COLON:
  344.                     Stack[ParserStackPointer-1] -> Right =
  345.                                   Stack[ParserStackPointer];
  346.                             Stack[ParserStackPointer-1] -> Left =
  347.                                   Stack[ParserStackPointer-2];
  348.                     Stack[ParserStackPointer-2] -> NodeKind -= 1000;
  349.                     Stack[ParserStackPointer] -> NodeKind -= 1000;
  350.                     Stack[ParserStackPointer-1] -> NodeKind += 1000;
  351.                     Stack[ParserStackPointer-2] =
  352.                         Stack[ParserStackPointer-1];
  353.                     ParserStackPointer -= 2;
  354.                             break;
  355.                         default:
  356.                 UpdateCharError("Found Operator ",
  357.                 Stack[ParserStackPointer-1] -> NodeKind);
  358.                 IPGlblParseError = IP_ERR_TWO_OPERAND;
  359.                 return NULL;
  360.             }
  361.             break;
  362.         default:
  363.             IPGlblParseError = IP_ERR_WRONG_SYNTAX;
  364.             return NULL;
  365.         }
  366.         }
  367.         else {         /* Push that token on stack - it is not handle yet. */
  368.         Stack[++ParserStackPointer] = ExprMalloc();
  369.  
  370. #        ifdef DEBUG1
  371.         if (MaxStackPointer < ParserStackPointer)
  372.             MaxStackPointer = ParserStackPointer;
  373. #        endif /* DEBUG1 */
  374.  
  375.             if (ParserStackPointer == MAX_PARSER_STACK-1) {
  376.                 IPGlblParseError = IP_ERR_STACK_OV;
  377.         return NULL;             /* We ignore data till now. */
  378.         }
  379.             if ((Stack[ParserStackPointer] -> NodeKind = Token) == USERINSTDEF)
  380.         Stack[ParserStackPointer] -> UserFunc = IPGlblUserFunc;
  381.             Stack[ParserStackPointer] -> PObj = NULL;
  382.         Stack[ParserStackPointer] -> Right =
  383.         Stack[ParserStackPointer] -> Left = NULL;
  384.         if (Token == NUMBER) {
  385.         Stack[ParserStackPointer] -> PObj = GenNUMValObject(Data);
  386.         }
  387.         else if (Token == PARAMETER) {
  388.         if ((Stack[ParserStackPointer] -> PObj =
  389.                 GetObject(IPGlblCharData)) == NULL) {
  390.             /*   Its new one - allocate memory for it. Create a      */
  391.             /* numeric object as a reasonable default.             */
  392.             Stack[ParserStackPointer] -> PObj =
  393.             IPAllocObject(IPGlblCharData, IP_OBJ_UNDEF, NULL);
  394.             InsertObject(Stack[ParserStackPointer] -> PObj);
  395.         }
  396.         Stack[ParserStackPointer] -> PObj -> Count++;
  397.         }
  398.         else if (Token == STRING) {
  399.         Stack[ParserStackPointer] -> PObj =
  400.             IPAllocObject("", IP_OBJ_STRING, NULL);
  401.         strcpy(Stack[ParserStackPointer] -> PObj -> U.Str,
  402.                IPGlblCharData);
  403.         Stack[ParserStackPointer] -> PObj -> Count++;
  404.         }
  405.             Token = GetToken(&Data);       /* And get new token from stream. */
  406.     }
  407.     }
  408. }
  409.  
  410. /*****************************************************************************
  411. *   Routine to test precedence of two tokens. returns 0, <0 or >0 according  *
  412. * to comparison results:                                                     *
  413. *****************************************************************************/
  414. static int TestPreceeding(int Token1, int Token2)
  415. {
  416.     int Preced1 = 0,
  417.     Preced2 = 0;
  418.  
  419.     if ((Token1 >= 1000) || (Token2 >= 1000))
  420.     return FALSE;                     /* Ignore sub-expr. */
  421.  
  422.     if (IS_FUNCTION(Token1))
  423.     Preced1 = 160;
  424.     else {
  425.         switch (Token1) {
  426.         case TOKENSTART:
  427.         case TOKENEND:
  428.             Preced1 = 10;
  429.             break;
  430.         case OPENPARA:
  431.             Preced1 = 20;
  432.             break;
  433.             case COMMA:
  434.             Preced1 = 30;
  435.             break;
  436.             case COLON:
  437.             Preced1 = 40;
  438.             break;
  439.         case BOOL_AND:
  440.         case BOOL_OR:
  441.         Preced1 = 50;
  442.         break;
  443.             case EQUAL:
  444.         case CMP_EQUAL:
  445.         case CMP_NOTEQUAL:
  446.         case CMP_LSEQUAL:
  447.         case CMP_GTEQUAL:
  448.         case CMP_LESS:
  449.         case CMP_GREAT:
  450.             Preced1 = 55;
  451.             break;
  452.         case PLUS:
  453.         case MINUS:
  454.             Preced1 = 80;
  455.             break;
  456.         case MULT:
  457.         case DIV:
  458.             Preced1 = 100;
  459.             break;
  460.         case POWER:
  461.             Preced1 = 120;
  462.             break;
  463.         case UNARMINUS:
  464.         case BOOL_NOT:
  465.             Preced1 = 125;
  466.             break;
  467.             case NUMBER:
  468.             case PARAMETER:
  469.             case STRING:
  470.         case CLOSPARA:
  471.             Preced1 = 180;
  472.             break;
  473.  
  474.         }
  475.     }
  476.  
  477.     if (IS_FUNCTION(Token2))
  478.     Preced2 = 150;
  479.     else {
  480.         switch (Token2) {
  481.         case TOKENSTART:
  482.         case TOKENEND:
  483.             Preced2 = 0;
  484.             break;
  485.         case CLOSPARA:
  486.             Preced2 = 15;
  487.             break;
  488.         case COMMA:
  489.             Preced2 = 30;
  490.             break;
  491.         case COLON:
  492.             Preced2 = 40;
  493.             break;
  494.         case BOOL_AND:
  495.         case BOOL_OR:
  496.         Preced2 = 50;
  497.         break;
  498.         case EQUAL:
  499.         case CMP_EQUAL:
  500.         case CMP_NOTEQUAL:
  501.         case CMP_LSEQUAL:
  502.         case CMP_GTEQUAL:
  503.         case CMP_LESS:
  504.         case CMP_GREAT:
  505.             Preced2 = 55;
  506.             break;
  507.         case PLUS:
  508.         case MINUS:
  509.             Preced2 = 70;
  510.             break;
  511.         case MULT:
  512.         case DIV:
  513.             Preced2 = 90;
  514.             break;
  515.         case POWER:
  516.             Preced2 = 110;
  517.             break;
  518.         case UNARMINUS:
  519.         case BOOL_NOT:
  520.             Preced2 = 130;
  521.             break;
  522.         case NUMBER:
  523.         case PARAMETER:
  524.         case STRING:
  525.         case OPENPARA:
  526.             Preced2 = 170;
  527.             break;
  528.  
  529.         }
  530.     }
  531.  
  532.     return Preced1 - Preced2 > 0;
  533. }
  534.  
  535. /*****************************************************************************
  536. *  Routine to update the character error message according to StrMsg & Token *
  537. *****************************************************************************/
  538. void UpdateCharError(char *StrMsg, int Token)
  539. {
  540.     char
  541.     *TokenChar = NULL;
  542.  
  543.     if (Token > 1000)
  544.     Token -= 1000;
  545.  
  546.     if (IS_NUM_FUNCTION(Token))
  547.     TokenChar = NumFuncTable[Token - NUM_FUNC_OFFSET].FuncName;
  548.     else if (IS_OBJ_FUNCTION(Token))
  549.     TokenChar = ObjFuncTable[Token - OBJ_FUNC_OFFSET].FuncName;
  550.     else if (IS_GEN_FUNCTION(Token))
  551.     TokenChar = GenFuncTable[Token - GEN_FUNC_OFFSET].FuncName;
  552.     else {
  553.         switch (Token) {
  554.             case PLUS:
  555.             TokenChar = "+";
  556.             break;
  557.             case MINUS:
  558.             TokenChar = "-";
  559.             break;
  560.         case MULT:
  561.             TokenChar = "*";
  562.             break;
  563.         case DIV:
  564.             TokenChar = "/";
  565.             break;
  566.         case POWER:
  567.             TokenChar = "^";
  568.             break;
  569.         case UNARMINUS:
  570.             TokenChar = "(Unar) -";
  571.             break;
  572.         case COMMA:
  573.             TokenChar = ",";
  574.             break;
  575.         case EQUAL:
  576.             TokenChar = "=";
  577.             break;
  578.         case COLON:
  579.             TokenChar = ":";
  580.             break;
  581.         case CMP_EQUAL:
  582.             TokenChar = "==";
  583.             break;
  584.         case CMP_NOTEQUAL:
  585.             TokenChar = "!=";
  586.             break;
  587.         case CMP_LSEQUAL:
  588.             TokenChar = "<=";
  589.             break;
  590.         case CMP_GTEQUAL:
  591.             TokenChar = ">=";
  592.             break;
  593.         case CMP_LESS:
  594.             TokenChar = "<";
  595.             break;
  596.         case CMP_GREAT:
  597.             TokenChar = ">";
  598.             break;
  599.         case BOOL_AND:
  600.             TokenChar = "&&";
  601.         break;
  602.         case BOOL_OR:
  603.             TokenChar = "||";
  604.         break;
  605.         case BOOL_NOT:
  606.             TokenChar = "!";
  607.         break;
  608.         case USERINSTDEF:
  609.         case USERFUNCDEF:
  610.         case USERPROCDEF:
  611.         TokenChar = "UserFunc";
  612.         break;
  613.         default:
  614.             sprintf(IPGlblCharData, "%s - Token %d\n", StrMsg, Token);
  615.             return;
  616.         }
  617.     }
  618.     sprintf(IPGlblCharData, "%s%s", StrMsg, TokenChar);
  619. }
  620.  
  621. /*****************************************************************************
  622. *   Routine to get the next token out of the expression.                     *
  623. * Gets the expression in S, and current position in i.                       *
  624. * Returns the next token found, set data to the returned value (if any),     *
  625. * and update i to one char ofter the new token found.                        *
  626. *   Note that in minus sign case, it is determined whether it is monadic or  *
  627. * diadic minus by the last token - if the last token was operator or '('     *
  628. * it is monadic minus.                                                       *
  629. *****************************************************************************/
  630. static int GetToken(RealType *Data)
  631. {
  632.     int i,
  633.     RetVal = TOKEN_NONE;
  634.     char c;
  635.  
  636.     while (isspace(c = InptPrsrGetC()));           /* Skip white blanks. */
  637.  
  638.     if (c == '"') {          /* Its a string token - read up to next ". */
  639.     i = 0;
  640.     while ((IPGlblCharData[i] = InptPrsrGetC()) != '"') {
  641.         if (IPGlblCharData[i] == '\\') /* Its escape char. for next one: */
  642.         IPGlblCharData[i] = InptPrsrGetC();
  643.         if (IPGlblCharData[i] < ' ' || i > LINE_LEN - 2) {
  644.         RetVal = TOKENERROR;
  645.         IPGlblCharData[i] = 0;
  646.         IPGlblParseError = IP_ERR_STR_TOO_LONG;
  647.         break;
  648.         }
  649.         i++;
  650.     }
  651.     if (RetVal != TOKENERROR) {
  652.         IPGlblCharData[i] = 0;
  653.         RetVal = STRING;
  654.     }
  655.     }
  656.     else if (isalpha(c)) {          /* Is it a variable/function name? */
  657.     if (islower(c))
  658.         IPGlblCharData[i=0] = toupper(c);
  659.     else
  660.         IPGlblCharData[i=0] = c;
  661.  
  662.     while (isalpha(c = InptPrsrGetC()) || isdigit(c) || c == '_')
  663.         if (islower(c))
  664.         IPGlblCharData[++i] = toupper(c);
  665.         else
  666.         IPGlblCharData[++i] = c;
  667.     IPGlblCharData[++i] = 0;
  668.     InptPrsrUnGetC(c);
  669.  
  670.     if ((int) strlen(IPGlblCharData) >= OBJ_NAME_LEN) {
  671.         RetVal = TOKENERROR;
  672.         IPGlblParseError = IP_ERR_NAME_TOO_LONG;
  673.     }
  674.     else {
  675.         RetVal = GetVarFuncToken(IPGlblCharData, Data);
  676.     }
  677.     }
  678.     else if (isdigit(c) || (c == '.')) {          /* Is it numeric data? */
  679.     IPGlblCharData[i=0] = c;
  680.  
  681.     while (isdigit(c = InptPrsrGetC()) || (c == '.') ||
  682.                     (c == 'e') || (c == 'E') || (c == 'e'))
  683.         IPGlblCharData[++i] = c;
  684.     /* Handle the special case of negative exponent ("111.111E-22"). */
  685.     if (c == '-' && (IPGlblCharData[i] == 'e' ||
  686.              IPGlblCharData[i] == 'E')) {
  687.         IPGlblCharData[++i] = c;
  688.         while (isdigit(c = InptPrsrGetC()) || (c == '.'))
  689.         IPGlblCharData[++i] = c;
  690.     }
  691.     IPGlblCharData[++i] = 0;
  692.  
  693.     InptPrsrUnGetC(c);
  694.  
  695. #    ifdef DOUBLE
  696.         sscanf(IPGlblCharData, "%lf", Data);
  697. #    else
  698.         sscanf(IPGlblCharData, "%f", Data);
  699. #    endif /* DOUBLE */
  700.  
  701.         RetVal = NUMBER;
  702.     }
  703.     else
  704.     switch (c) {
  705.         case '+':
  706.         RetVal = PLUS;
  707.         break;
  708.         case '-':
  709.         switch (IPGlblILastToken) {
  710.             case 0:          /* If first token (no last token yet). */
  711.             case PLUS:
  712.             case MINUS:
  713.             case MULT:
  714.             case DIV:
  715.             case POWER:
  716.             case COMMA:
  717.             case EQUAL:
  718.             case CMP_EQUAL:
  719.             case CMP_NOTEQUAL:
  720.             case CMP_LSEQUAL:
  721.             case CMP_GTEQUAL:
  722.             case CMP_LESS:
  723.             case CMP_GREAT:
  724.             case BOOL_AND:
  725.             case BOOL_OR:
  726.             case BOOL_NOT:
  727.             case COLON:
  728.             case UNARMINUS:
  729.             case OPENPARA:
  730.             RetVal = UNARMINUS;
  731.             break;
  732.             default:
  733.                         RetVal = MINUS;
  734.                 break;
  735.         }
  736.         break;
  737.         case '*':
  738.         RetVal = MULT; break;
  739.         case '/':
  740.         RetVal = DIV;
  741.         break;
  742.         case '^':
  743.         RetVal = POWER;
  744.         break;
  745.         case '(':
  746.         RetVal = OPENPARA;
  747.         break;
  748.         case ')':
  749.         RetVal = CLOSPARA;
  750.         break;
  751.         case '=':
  752.         switch (c = InptPrsrGetC()) {
  753.             case '=':
  754.                 RetVal = CMP_EQUAL;
  755.             break;
  756.             default:
  757.             InptPrsrUnGetC(c);
  758.             RetVal = EQUAL;
  759.             break;
  760.         }
  761.         break;
  762.         case '<':
  763.         switch (c = InptPrsrGetC()) {
  764.             case '=':
  765.                 RetVal = CMP_LSEQUAL;
  766.             break;
  767.             default:
  768.             InptPrsrUnGetC(c);
  769.             RetVal = CMP_LESS;
  770.             break;
  771.         }
  772.         break;
  773.         case '>':
  774.         switch (c = InptPrsrGetC()) {
  775.             case '=':
  776.                 RetVal = CMP_GTEQUAL;
  777.             break;
  778.             default:
  779.             InptPrsrUnGetC(c);
  780.             RetVal = CMP_GREAT;
  781.             break;
  782.         }
  783.         break;
  784.         case '&':
  785.         if ((c = InptPrsrGetC()) == '&') {
  786.             RetVal = BOOL_AND;
  787.             break;
  788.         }
  789.         else {
  790.             RetVal = TOKENERROR;
  791.             IPGlblCharData[0] = '&';
  792.             IPGlblCharData[1] = c;
  793.             IPGlblCharData[2] = 0;
  794.             IPGlblParseError = IP_ERR_UNDEF_TOKEN;
  795.             break;
  796.         }
  797.         case '|':
  798.         if ((c = InptPrsrGetC()) == '|') {
  799.             RetVal = BOOL_OR;
  800.             break;
  801.         }
  802.         else {
  803.             RetVal = TOKENERROR;
  804.             IPGlblCharData[0] = '|';
  805.             IPGlblCharData[1] = c;
  806.             IPGlblCharData[2] = 0;
  807.             IPGlblParseError = IP_ERR_UNDEF_TOKEN;
  808.             break;
  809.         }
  810.         case ',':
  811.         RetVal = COMMA;
  812.         break;
  813.         case ':':
  814.         RetVal = COLON;
  815.         break;
  816.         case ';':
  817.         RetVal = TOKENEND;
  818.         break;           /* End of expression! */
  819.         case '!':
  820.         if ((c = InptPrsrGetC()) == '=') {
  821.             RetVal = CMP_NOTEQUAL;
  822.         }
  823.         else {
  824.             InptPrsrUnGetC(c);
  825.             RetVal = BOOL_NOT;
  826.         }
  827.         break;
  828.         default:
  829.         RetVal = TOKENERROR;
  830.         IPGlblCharData[0] = c;
  831.         IPGlblCharData[1] = 0;
  832.         IPGlblParseError = IP_ERR_UNDEF_TOKEN;
  833.         break;
  834.     }
  835.  
  836.     IPGlblILastToken = RetVal;
  837.  
  838.     return RetVal;
  839. }
  840.  
  841. /*****************************************************************************
  842. *   Routine to test alpha Token for match with one of the defined functions  *
  843. * and returns that Token function if found one. otherwise it is assumed to   *
  844. * be a variable (new or old).                             *
  845. * Note that although the search is linear, It is extremely fast to add new   *
  846. * functions - simply add its token, its entry here, and in the parser itself.*
  847. *****************************************************************************/
  848. static int GetVarFuncToken(char *Token, RealType *Data)
  849. {
  850.     int i;
  851.     char c;
  852.     UserDefinedFuncDefType *UserFunc;
  853.  
  854.     if (strcmp("COMMENT", Token) == 0) {
  855.     /* Get first nonspace char after the COMMENT key word: */
  856.     while (isspace(c = InptPrsrGetC()));
  857.     /* And read the input until this char appear again (end of comment): */
  858.     while (c != InptPrsrGetC());
  859.  
  860.     return GetToken(Data);               /* Return next token instead. */
  861.     }
  862.  
  863.     for (UserFunc = UserDefinedFuncList;
  864.      UserFunc != NULL;
  865.      UserFunc = UserFunc -> Pnext)
  866.     if (strcmp(UserFunc -> FuncName, Token) == 0) {
  867.         while (isspace(c = InptPrsrGetC()));       /* Skip white blanks. */
  868.         InptPrsrUnGetC(c);
  869.         if (c == '(') {
  870.         IPGlblUserFunc = UserFunc;
  871.         return USERINSTDEF;
  872.         }
  873.         else
  874.         break;
  875.     }
  876.     for (i = 0; i < NumFuncTableSize; i++)        /* Is it Numeric function? */
  877.     if (strcmp(NumFuncTable[i].FuncName, Token) == 0)
  878.         return NumFuncTable[i].FuncToken;
  879.     for (i = 0; i < ObjFuncTableSize; i++)       /* Is it Object function? */
  880.     if (strcmp(ObjFuncTable[i].FuncName, Token) == 0)
  881.         return ObjFuncTable[i].FuncToken;
  882.     for (i = 0; i < GenFuncTableSize; i++)      /* Is it General function? */
  883.     if (strcmp(GenFuncTable[i].FuncName, Token) == 0)
  884.         return GenFuncTable[i].FuncToken;
  885.  
  886.     if (strcmp("FUNCTION", Token) == 0)
  887.     return USERFUNCDEF;
  888.     if (strcmp("PROCEDURE", Token) == 0)
  889.     return USERPROCDEF;
  890.     
  891.     for (i = 0; i < ConstantTableSize; i++)/* Replace constant by its value. */
  892.     if (strcmp(ConstantTable[i].FuncName, Token) == 0) {
  893.         sprintf(Token, "%lg", ConstantTable[i].Value);
  894.         *Data = ConstantTable[i].Value;
  895.         return NUMBER;
  896.     }
  897.  
  898.     return PARAMETER;   /* If not a function - it is assumed to be variable. */
  899. }
  900.  
  901. static char UnGetChar;
  902.  
  903. /*****************************************************************************
  904. *   Routine to get the value to EchoSrc variable.                 *
  905. *****************************************************************************/
  906. static int GetEchoSrc(void)
  907. {
  908.     int EchoSrc;
  909.     IPObjectStruct
  910.     *PObj = GetObject("ECHOSRC");
  911.  
  912.     if (PObj == NULL || !IP_IS_NUM_OBJ(PObj)) {
  913.     WndwInputWindowPutStr("No numeric object name EchoSrc is defined");
  914.     EchoSrc = DEFAULT_ECHOSRC;
  915.     }
  916.     else
  917.     EchoSrc = (int) (PObj -> U.R);
  918.  
  919.     return EchoSrc;
  920. }
  921.  
  922. /*****************************************************************************
  923. *   Routine to control all getchar in this module and echo it if requested   *
  924. * Note it handles the FileStack and decrease it if end of file was found.    *
  925. *****************************************************************************/
  926. static char InptPrsrGetC(void)
  927. {
  928.     static char *p,
  929.     Line[INPUT_LINE_LEN] = "",
  930.     TLine[INPUT_LINE_LEN] = "";
  931.     static int
  932.     LineLength = 0,
  933.     LineCount = 0;
  934.     char c;
  935.     int i;
  936.  
  937.     if (UnGetChar == 0) {               /* One level of unget char... */
  938.     if (LineCount < LineLength) {     /* Is there anything in local Line? */
  939.     }
  940.     else
  941.         do {
  942.             if (FileStackPtr == 0) {
  943.             WndwInputWindowGetStr(Line, INPUT_LINE_LEN);
  944.             LineCount = 0;
  945.             }
  946.             else {
  947.             sprintf(Line, "%s > ", FileStack[FileStackPtr-1].Name);
  948.             LineCount = strlen(Line);
  949.             if (fgets(TLine, INPUT_LINE_LEN,
  950.                     FileStack[FileStackPtr-1].f) == NULL) {
  951.                 /* Its end of file - close it and update stack. */
  952.             TLine[0] = 0;
  953.                 fclose(FileStack[--FileStackPtr].f);
  954.             }
  955.  
  956.             /* Strip off CR/LF/TAB.  */
  957.             for (i = LineCount, p = TLine; *p != 0; p++) {
  958.             if (*p == 0x09)
  959.                 do {
  960.                 Line[i++] = ' ';
  961.                 }
  962.                     while ((i - LineCount) % 8 != 0);
  963.             else if (*p < ' ' || *p > '~')
  964.                 break;
  965.             else
  966.                 Line[i++] = *p;
  967.             }
  968.             Line[i] = 0;
  969.             }
  970.  
  971.         if (GetEchoSrc())
  972. #ifdef DJGCC
  973.             WndwInputWindowPutStr(Line);
  974. #else
  975.             if (FileStackPtr != 0)     /* Input was from keyboard? */
  976.             WndwInputWindowPutStr(Line);
  977. #endif /* DJGCC */
  978.  
  979.             LineLength = strlen(Line);
  980.         } while (LineCount >= LineLength);
  981.  
  982.     c = Line[LineCount++];
  983.     if (c == '#') {              /* Its a comment - skip that line. */
  984.             c = ' ';                   /* Must return something. */
  985.             LineCount = LineLength;    /* Force next time to fetch new line. */
  986.     }
  987. #    ifdef DEBUG1
  988.         fprintf(stderr, "%c", c);
  989. #    endif /* DEBUG1 */
  990.     }
  991.     else {
  992.     c = UnGetChar;
  993.     UnGetChar = 0;
  994.     }
  995.  
  996.     return c;
  997. }
  998.  
  999. /*****************************************************************************
  1000. *   Routine to unget one char                             *
  1001. *****************************************************************************/
  1002. static void InptPrsrUnGetC(char c)
  1003. {
  1004.     UnGetChar = c;
  1005. }
  1006.  
  1007. /*****************************************************************************
  1008. *   Routine to read data up to the next end of expression marker - ';'.         *
  1009. *****************************************************************************/
  1010. void FlushToEndOfExpr(int FlushStdin)
  1011. {
  1012.     if (FileStackPtr > 0)    /* Close all the open files - back to stdin. */
  1013.     while (FileStackPtr)
  1014.         fclose(FileStack[--FileStackPtr].f);
  1015.     else if (FlushStdin && IPGlblILastToken != TOKENEND)
  1016.     while (InptPrsrGetC() != ';');
  1017.  
  1018. }
  1019.  
  1020. /*****************************************************************************
  1021. *   Routine to push new file to read on the FileStack from INCLUDE command:  *
  1022. *****************************************************************************/
  1023. void FileInclude(char *PrmFileName)
  1024. {
  1025.     int i;
  1026.     FILE *f;
  1027.     char s[LINE_LEN], FileName[LINE_LEN], c, *p;
  1028.  
  1029.     if (FileStackPtr < FILE_STACK_SIZE) {
  1030.     strcpy(FileName, PrmFileName);
  1031.  
  1032.     for (i = 0; i < strlen(FileName); i++)
  1033.         if (isupper(FileName[i]))
  1034.             FileName[i] = tolower(FileName[i]);
  1035.  
  1036.       if ((p = strstr(FileName, ".irt")) == NULL)
  1037.         strcat(FileName, ".irt");
  1038.  
  1039.     if ((f = fopen(FileName, "r")) != NULL) {
  1040.         FileStack[FileStackPtr].f = f;
  1041.         for (i = strlen(FileName)-1;       /* Isolate the file name. */
  1042.          i > 0 && (c = FileName[i]) != '\\' && c != '/' && c != ':';
  1043.          i--);
  1044.         if (i > 0)
  1045.         i++;
  1046.         strncpy(FileStack[FileStackPtr].Name, &FileName[i],
  1047.                             FILE_NAME_LEN-1);
  1048.         FileStackPtr++;         /* Now next char is from that file! */
  1049.     }
  1050.     else {
  1051.         sprintf(s, "Cannt open file %s - ignored", FileName);
  1052.         WndwInputWindowPutStr(s);
  1053.     }
  1054.     }
  1055.     else
  1056.     WndwInputWindowPutStr("File nesting too deep - ignored");
  1057. }
  1058.  
  1059. /*****************************************************************************
  1060. *   Routine to return parsing error if happen one, zero    elsewhere         *
  1061. *****************************************************************************/
  1062. InptPrsrEvalErrType InptPrsrParseError(char **Message)
  1063. {
  1064.     InptPrsrEvalErrType Temp;
  1065.  
  1066.     *Message = IPGlblCharData;
  1067.     Temp = IPGlblParseError;
  1068.     IPGlblParseError = IPE_NO_ERR;
  1069.     return Temp;
  1070. }
  1071.